/*
 * @Author: Wangjun
 * @Date: 2023-03-27 09:10:59
 * @LastEditTime: 2024-07-29 17:20:16
 * @LastEditors: wangjun haodreams@163.com
 * @Description:
 * @FilePath: \libs\axis\axisy.go
 * hnxr
 */
package span

import (
	"errors"
	"image/color"
	"math"
)

/**
 * @description: 转换Y轴坐标, 根据窗口高度计算对应的变比
 * @param {int} height 窗口的底部高度 (底部-顶部值)
 * @param {*} max 需要计算的最大值
 * @param {float64} min 需要计算的最小值
 * @return {*} k 坐标对应系数 数据还原时格式如下 y=(max-value) * k
 */
func TranslateY[G float64 | float32 | int](height, max, min G) (k G) {
	if max == min {
		return
	}
	k = height / (max - min)
	return
}

/**
 * @description:
 * @param {*} y 当前坐标y轴位置
 * @param {*} bottom 窗口的底部位置 (y2)
 * @param {*}  k 坐标对应系数
 * @return {*}
 */
func ReverseY[G float64 | float32 | int](height, y, k G) (y1 G) {
	return (height - y) / k
}

type Offset struct {
	Top    int
	Bottom int
	Left   int
	Right  int
}

type Point struct {
	X int64
	Y float64
}
type TimePath struct {
	Width     int
	Height    int
	LineWidth float64
	k         float64
	Min       float64
	Max       float64
	Name      string
	Desc      string
	Color     color.RGBA
	*Offset
	RawSpan
	Ptr  any
	Show bool
	Y    []float64 //[Width] float64 , 每个宽度一个数据
}

func NewTimePath(offset *Offset) *TimePath {
	m := new(TimePath)
	m.LineWidth = 1
	m.Offset = offset
	if offset == nil {
		m.Offset = new(Offset)
	}
	m.Precision = 3 //默认保留3位小数
	m.Color = color.RGBA{255, 0, 0, 255}
	return m
}

type TimePoint struct {
	Y float64
	X int64
}

func (m *TimePath) SetName(name string) *TimePath {
	m.Name = name
	return m
}

func (m *TimePath) SetDesc(desc string) *TimePath {
	m.Desc = desc
	return m
}

// 获取指定位置的时间
func (m *TimePath) GetTime(x int) int64 {
	x -= m.Left
	if x < 0 || x >= m.Width {
		return 0
	}
	return m.RawSpan.GetTime(x)
}

// 获取某个坐标的值
func (m *TimePath) GetValue(x int) float64 {
	x -= m.Left
	if x < 0 || x >= m.Width {
		return math.NaN()
	}
	if x >= len(m.RawSpan.fvals) {
		return math.NaN()
	}
	return m.fvals[x]
}

func (m *TimePath) SetWidth(w int) *TimePath {
	m.Width = w - m.Right - m.Left
	if m.Width < 0 {
		m.Width = 0
	}
	return m
}

// 设置高度
func (m *TimePath) SetHeight(h int) *TimePath {
	m.Height = h - m.Top - m.Bottom
	return m
}

func (m *TimePath) Resize(w, h int) *TimePath {
	m.SetWidth(w)
	m.SetHeight(h)
	m.Update()
	return m
}

// 设置颜色
func (m *TimePath) SetColor(c color.RGBA) *TimePath {
	m.Color = c
	return m
}

// 获取浮点型数据列表
func (m *TimePath) GetFVals() []float64 {
	return m.Y
}

// 设置时间
func (m *TimePath) Reset(beginTime, endTime int64) *TimePath {
	if beginTime >= endTime {
		m.Error = errors.New("开始时间必须小于结束时间")
		return m
	}
	if m.Width == 0 {
		return m
	}
	inverter := int64(math.Ceil(float64(endTime-beginTime) * 4 / float64(m.Width)))
	m.RawSpan.Reset(beginTime, endTime, inverter)
	return m
}

// beginTime 开始时间
// endTime 结束时间
func (m *TimePath) SetTimePoint(beginTime, endTime int64, tps []*TimePoint) *TimePath {
	if m.Width == 0 || m.Height == 0 {
		m.Error = errors.New(" Width and Height should be set before SetTimePoint")
		return m
	}
	if m.Reset(beginTime, endTime).Error != nil {
		return m
	}

	for _, p := range tps {
		m.Put(p.X, p.Y)
	}

	m.Update()
	return m
}

/**
 * @description: 获取数据数组
 * @return {*}
 */
func (m *TimePath) Update() *TimePath {
	m.RawSpan.Update()
	m.Min = math.NaN()
	m.Max = m.Min
	for _, v := range m.fvals {
		if math.IsNaN(v) || math.IsInf(v, 0) {
			continue
		}
		if math.IsNaN(m.Min) {
			m.Min = v
			m.Max = v
			continue
		}
		if m.Max < v {
			m.Max = v
		}
		if m.Min > v {
			m.Min = v
		}
	}
	if math.IsNaN(m.Min) {
		m.Max = 0
		m.Min = 0 // 最小值设置为0 防止k=0
	}
	if m.Max == m.Min {
		m.Max++
		m.Min--
	}
	k := (m.Max - m.Min) * 0.03
	m.Max += k
	m.Min -= k
	m.k = float64(m.Height) / (m.Max - m.Min)

	if cap(m.Y) < len(m.fvals) {
		m.Y = make([]float64, len(m.fvals))
	} else {
		m.Y = m.Y[:len(m.fvals)]
	}
	for i, v := range m.fvals {
		if math.IsNaN(v) {
			m.Y[i] = v
			continue
		}
		m.Y[i] = float64(m.Height) - (v-m.Min)*m.k
		//m.Y[i] = float64(m.Height-i%m.Height) + float64(m.Bottom+m.Top)
	}
	return m
}
